<?php

namespace App\Repositories;

use App\Exceptions\UploadFileFailException;
use Exception;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use App\Models\Picture;
use App\Models\PictureMapping;
use Illuminate\Support\Facades\Log;
use Qcloud\Cos\Client;
use Qcloud\Cos\Copy;
use Qcloud\Cos\ImageParamTemplate\BlindWatermarkTemplate;
use Qcloud\Cos\ImageParamTemplate\ImageWatermarkTemplate;
use Qcloud\Cos\ImageParamTemplate\PicOperationsTransformation;
use Qcloud\Cos\ImageParamTemplate\TextWatermarkTemplate;
use zgldh\QiniuStorage\QiniuStorage;

class FileRepository
{
    public function __construct()
    {
    }

    /**
     * overwrite_path_by_object根据$object_type, $object_id去覆盖文件且更新file表
     * @param \Illuminate\Http\UploadedFile $uploadFile
     * @param string $objectType
     * @param int $objectId
     * @param null $subObjectKey
     * @return array
     * @throws \App\Exceptions\UploadFileFailException
     */
    public function upload($uploadFile, $file_type = '', $objectId = 0, $fileName = null): array
    {
        // 括号里面的是必须加的哦 如果括号里面的不加上的话，下面的方法也无法调用的
        if (!$uploadFile->isValid()) {
            throw new UploadFileFailException('file is invalid');
        }

        $path = $uploadFile->getRealPath();
        $originName = $fileName ? $fileName : $uploadFile->getClientOriginalName();
        $driver = env('FILESYSTEM_DRIVER');

        $file['uid'] = UID;
        $file['md5'] = md5_file($path);
        $file['sha1'] = sha1_file($path);
        $file['name'] = $originName;
        $file['size'] = $uploadFile->getSize();
        // $file['mimetype'] = $uploadFile->getClientMimeType();
        $file['ext'] = $uploadFile->getClientOriginalExtension();
        // $file['location'] = $driver;

        $ff = Picture::query()
            ->where('md5', $file['md5'])
            ->first();
        if (!$ff) {
            switch ($driver) {
                case Picture::DRIVER_QINIU:
                    $res = $this->uploadToQiniu($uploadFile);
                    break;
                case Picture::DRIVER_OSS:
                    $res = $this->uploadToOss($uploadFile, $file_type);
                    break;
                case Picture::DRIVER_TENCENT_COS:
                    $res = $this->uploadToCos($uploadFile, $file_type);
                    break;
                case Picture::DRIVER_LOCAL:
                case 'public':
                    $res = $this->uploadToLocal($uploadFile, $file_type);
                    break;
                default:
                    throw new Exception('upload driver error: ' . $driver);
            }
        }

        if (!$ff && !$res['success']) {
            throw new UploadFileFailException('upload file fails');
        }

        if (!$ff) {
            $file['create_time'] = time();
            $file['path'] = $res['path'];
            $file_id = Picture::query()->insertGetId($file);
        } else {
            $file_id = $ff['id'];

            if ($ff['deleted_at']) {
                Picture::withTrashed()->where('id', $file_id)->update(['deleted_at' => null, 'uid' => UID]);
            }

            if ($ff['name'] !== $originName) {
                // 同样的文件，文件名不一样，更新成新名字
                Picture::query()->where('id', $file_id)->update(['name' => $originName, 'uid' => UID]);
            }
        }

        if (!$file_id) {
            throw new \Exception('文件上传失败');
        }

        // 不写入mapping，直接返回
        $res = Picture::query()->find($file_id)->toArray();
        $res['full_path'] = image_path($res['path']);
        return $res;
    }

    public function uploadToOss(UploadedFile $file, $type = "")
    {
        $ext = $file->getClientOriginalExtension();

        //获取文件的临时绝对路径
        $path = $file->getRealPath();

        // 按文件md5 定义文件名
        $md5 = md5_file($path);
        $filename = env('OSS_UPLOAD_PATH');
        if ($type) {
            $filename .= $type . '/';
        }
        $filename .= date('Ymd') . '/' . $md5 . '.' . $ext;

        $res = false;
        try {
            $disk = Storage::disk(Picture::DRIVER_OSS);
            $res = $disk->put($filename, file_get_contents($path));
        } catch (\League\Flysystem\Exception $e) {
            Log::error(
                '[FileRepository] file put error',
                [
                    'messages' => $e->getMessage() . '::' . json_encode(Request()->all()),
                    'code' => $e->getCode(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ]
            );
        }

        if ($res) {
            return ['success' => true, 'path' => $filename];
        } else {
            return ['success' => false, 'msg' => '上传失败'];
        }
    }

    public function uploadToCos(UploadedFile $file, $type = "")
    {
        $ext = $file->getClientOriginalExtension();

        //获取文件的临时绝对路径
        $path = $file->getRealPath();

        // 按文件md5 定义文件名
        $md5 = md5_file($path);
        $filename = env('COSV5_UPLOAD_PATH');
        if ($type) {
            $filename .= $type . '/';
        }
        $filename .= date('Ymd') . '/' . $md5 . '.' . $ext;

        $res = false;
        try {
            $disk = Storage::disk(Picture::DRIVER_TENCENT_COS);
            $res = $disk->put($filename, file_get_contents($path));
        } catch (\League\Flysystem\Exception $e) {
            Log::error(
                '[FileRepository] file put error',
                [
                    'messages' => $e->getMessage() . '::' . json_encode(Request()->all()),
                    'code' => $e->getCode(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ]
            );
        }

        if ($res) {
            return ['success' => true, 'path' => $filename];
        } else {
            return ['success' => false, 'msg' => '上传失败'];
        }
    }

    /**
     * 上传图片并加水印
     * @param string $path 图片路径
     * @param string $waterTitle 水印标题
     * @return string|void
     */
    public function writeWater($path = "", $waterTitle = "本硕博联谊服务号")
    {
        $cosClient = new Client([
            'region' => env('COSV5_REGION', 'ap-guangzhou'),
            'schema' => 'https', //协议头部，默认为http
            'credentials' => array(
                'secretId' => env('COSV5_SECRET_ID'),
                'secretKey' => env('COSV5_SECRET_KEY')
            )
        ]);

        $filename = substr($path, strripos($path, "/") + 1);
        $imgPath = substr($path, 0, strripos($path, "/") + 1);
        $imgPath = str_replace("Uploads/", "Uploads/water/", $imgPath) . $filename;
        $local_path = public_path("uploads") . '/water/' . $filename;

        $imageWatermarkTemplate = new ImageWatermarkTemplate();
        $imageWatermarkTemplate->setImage(env('COSV5_DOMAIN') . "/Uploads/logo.png");
        $imageWatermarkTemplate->setGravity('southeast');
        $imageWatermarkTemplate->setDx(60);
        $imageWatermarkTemplate->setDy(60);
//        $imageWatermarkTemplate->setBlogo(1);
        $imageWatermarkTemplate->setScatype(1);
//        $imageWatermarkTemplate->setSpcent(3);
        try {
            $result = $cosClient->getObject(array(
                'Bucket' => env('COSV5_BUCKET') . '-' . env('COSV5_APP_ID'), //存储桶名称，由BucketName-Appid 组成，可以在COS控制台查看 https://console.cloud.tencent.com/cos5/bucket
                'Key' => $path,
                'ImageHandleParam' => $imageWatermarkTemplate->queryString(),
                'SaveAs' => $local_path
            ))->toArray();

            /*
            $url = $result['Location'] . '?' . $imageWatermarkTemplate->queryString() . '/v=' . date("hi");
            echo '<a href="https://' . $url . '" target="_blank"> <img src="http://'.$url.'" style="height: 720px;" /></a><br/>';
            */
            $disk = Storage::disk(Picture::DRIVER_TENCENT_COS);
            $res = $disk->put($imgPath, file_get_contents($local_path));
            if (!$res) return false;

            unlink($local_path);
//            echo '<a href="https://file.bsbly.com' . $imgPath . '" target="_blank">' . $imgPath . '</a>';
            return $imgPath;
        } catch (\League\Flysystem\Exception $e) {
            return false;
        }
        return true;
    }

    /**
     * 识别微信二维码
     */
    public function checkWxQrcode($path = null)
    {
        if (empty($path)) return null;
        $cosClient = new Client([
            'region' => env('COSV5_REGION', 'ap-guangzhou'),
            'schema' => 'https', //协议头部，默认为http
            'credentials' => array(
                'secretId' => env('COSV5_SECRET_ID'),
                'secretKey' => env('COSV5_SECRET_KEY')
            )
        ]);
        try {
            $result = $cosClient->Qrcode(array(
                'Bucket' => env('COSV5_BUCKET') . '-' . env('COSV5_APP_ID'),
                'Key' => $path
            ))->toArray();

            if (empty($result['QRcodeInfo']) || empty($result['QRcodeInfo'][0]['CodeUrl']))
                return false;

            // $codeUrl = $result['QRcodeInfo'][0]['CodeUrl'];
            if (strpos($result['QRcodeInfo'][0]['CodeUrl'], "u.wechat") > 0)
                return true;

            return false;
        } catch (\Exception $e) {
            return false;
        }
    }

    public function getFilesByObject($objectType, $objectId, $subObjectKey = 0)
    {
        if (!$objectType || !$objectId) {
            return false;
        }

        $res = FileMapping::query()
            ->join('file', 'file_mapping.file_id', '=', 'file.id')
            ->where('file_mapping.object_type', $objectType)
            ->where('file_mapping.object_id', $objectId)
            ->when($subObjectKey, function ($query) use ($subObjectKey) {
                return $query->where('file_mapping.sub_object_key', $subObjectKey);
            })
            ->whereNull('file.deleted_at')
            ->orderBy('file_mapping.sequence', 'desc')
            ->orderBy('file_mapping.id', 'desc')
            ->selectRaw('file.*,file_mapping.*')
            ->get();

        if ($res) {
            return $res->each(function (&$item) {
                return $item->full_path = image_path($item['path']);
            })->toArray();
        }

        return $res ? $res->toArray() : [];
    }

    public function getFileByMapId($mapId)
    {
        if (!$mapId) {
            return;
        }

        $res = FileMapping::query()
            ->select("file.*", "file_mapping.*")
            ->join('file', 'file_mapping.file_id', '=', 'file.id')
            ->where('file_mapping.id', $mapId)
            ->first()
            ->toArray();

        $res['full_path'] = image_path($res['path']);

        return $res;
    }

    public function getFileById($fileId)
    {
        return File::query()->where('id', $fileId)->first();
    }

    public function uploadToLocal($fileCharater, $type)
    {
        //获取文件的扩展名
        $ext = $fileCharater->getClientOriginalExtension();

        //获取文件的临时绝对路径
        $path = $fileCharater->getRealPath();

        // 按文件md5 定义文件名
        $md5 = md5_file($path);

        // 按日期创建文件夹 定义文件名
        $filename = '/Uploads/member/';
        if ($type) {
            $filename .= $type . '/';
        }
        $filename .= date('Ymd') . '/' . $md5 . '.' . $ext;

        //存储文件。disk里面的public。总的来说，就是调用disk模块里的public配置
        $res = Storage::disk('public')->put($filename, file_get_contents($path));


        if ($res) {
            return ['success' => true, 'path' => $filename];
        } else {
            return ['success' => false, 'msg' => '上传失败'];
        }
    }

    public function uploadToQiniu($fileCharater)
    {
        $ext = $fileCharater->getClientOriginalExtension();

        //获取文件的临时绝对路径
        $path = $fileCharater->getRealPath();

        // 按文件md5 定义文件名
        $disk = Storage::disk('qiniu');

        $md5 = md5_file($path);

        $filename = config('filesystems.disks.qiniu.file_dir') . $md5 . '.' . $ext;

        $res = $disk->put($filename, file_get_contents($path));

        if ($res) {
            return ['success' => true, 'path' => $filename];
        } else {
            return ['success' => false, 'msg' => '上传失败'];
        }
    }

    public function removeMapFile(int $mid)
    {
        return FileMapping::query()->where(['id' => $mid])->delete();
    }

    public function updateMapping(int $object_id, int $object_type = 1, array $file_ids = [])
    {
        FileMapping::where([
            'object_id' => $object_id,
            "object_type" => FileMapping::getObjectType($object_type)
        ])->delete();

        foreach ($file_ids as $k => $v) {
            FileMapping::create([
                'file_id' => $v,
                'object_id' => $object_id,
                'object_type' => FileMapping::getObjectType($object_type)
            ]);
        }
    }

    /**
     * 文件列表(搜索)
     * @param  [type] $request [description]
     * @return [type]          [description]
     */
    public function getFileBySearch($request)
    {
        return FileMapping::select([
            'file_mapping.*',
            'file.name',
            'file.path',
            'file.ext',
            'file.mimetype',
            'file.size',
        ])
            ->leftJoin('file', 'file_mapping.file_id', '=', 'file.id')
            ->where(function ($query) use ($request) {
                $query->when(isset($request['object_type']) && !empty($request['object_type']), function ($query) use ($request) {
                    return $query->where('file_mapping.object_type', $request['object_type']);
                })
                    ->when(isset($request['object_id']) && !empty($request['object_id']), function ($query) use ($request) {
                        if (is_array($request['object_id'])) {
                            return $query->whereIn('file_mapping.object_id', $request['object_id']);
                        } else {
                            return $query->where('file_mapping.object_id', intval($request['object_id']));
                        }
                    });
            })->orderBy('file_mapping.id', 'desc')->paginate(15);
    }

    /**
     * 保存到本地
     */
    public function saveLocalImg($url = '', $fileName = "", $type = true)
    {
        if (empty($url)) return "";
        $base_path = base_path();
        $public_path = public_path();
        if (stripos($url, 'http') === false) {
            return $url;
        }

        $image = '/uploads/tmp/' . date("Ymd") . '/tmp_' . ($fileName ?? date("Ymdhis")) . '.png';
        $imgPath = $public_path . $image;

        if (!file_exists($imgPath)) {
            if (!file_exists(dirname($imgPath))) {
                RecursiveMkdir(dirname($imgPath));
            }
        }

        if ($type) {
            $image_url = str_replace(array('%3A', '%2F'), array(':', '/'), urlencode($url));
        } else {
            $image_url = $url;
        }

        $image_content = file_get_contents($image_url);
        if (!$image_content) return "";
        $fimage = fopen($imgPath, 'w');
        fwrite($fimage, $image_content);
        fclose($fimage);

        return $image;
    }

    // 通过链接 http 获取
    public function saveUrlImage($url = '', $fileName = "", $uid = 0, $dir = "tmp")
    {
        if (empty($url)) return "";

        $base_path = base_path();
        $public_path = public_path();
        if (stripos($url, 'http') === false) {
            return $url;
        }

        $image = '/uploads/avatar_temp/' . date("Ymd") . '/avatar_' . $uid . '.png';
        $imgPath = $public_path . $image;

        if (!file_exists($imgPath)) {
            if (!file_exists(dirname($imgPath))) {
                RecursiveMkdir(dirname($imgPath));
            }
        }

        $image_url = str_replace(array('%3A', '%2F'), array(':', '/'), urlencode($url));
        $image_content = file_get_contents($image_url);
        if (!$image_content) return "";
        $fimage = fopen($imgPath, 'w');
        fwrite($fimage, $image_content);
        fclose($fimage);

        // 按文件md5 定义文件名
        $md5 = md5_file($imgPath);
        $filename = env('COSV5_UPLOAD_PATH') . $dir . '/' . date('Ymd') . '/' . ($fileName ?? $md5) . '.png';

        $disk = Storage::disk(Picture::DRIVER_TENCENT_COS);
        $res = $disk->put($filename, file_get_contents($imgPath));

        if ($res) {
            unlink($imgPath);//删除
            return $filename;
        } else {
            return "";
        }
    }

    /**
     * 小程序二维码推广码
     * @Author   thomashtao               bosslen
     * @DateTime 2022-09-01T15:39:20+0800
     * @param string $qrcode_content [description]
     * @return   [type]                                   [description]
     */
    public function uploadQrcodeToOss($qrcode_content = "", $fileName = "", $uid = "", $type = "", $content = null)
    {
        if (!$qrcode_content) return "";

        if ($type == 'path') {  //直接本地路径
            $root_dir = "avatar_qrcode";
            $imgPath = public_path($qrcode_content);
        } else {
            $image = '/uploads/qrcode/' . date("Ymd") . '/spread_' . $uid . '.png';
            $imgPath = public_path($image);
            if (!file_exists($imgPath)) {
                if (!file_exists(dirname($imgPath))) {
                    RecursiveMkdir(dirname($imgPath));
                }
            }

            $fimage = fopen($imgPath, 'w');
            fwrite($fimage, $qrcode_content);
            fclose($fimage);

            $root_dir = "qrcode";
            if ($type == 'spread') {  //推广覆盖底图
                coverImg($image, null, 217, 313); //图片覆盖二维码
                $root_dir = "qrcode_hb";
            } else if ($type == 'elect_pool') {
                $root_dir = "qrcode/qun";
                coverElectPool($image, null, ['sx' => 210, 'sy' => 580], "", $content);
            }
        }

        // 按文件md5 定义文件名
        $md5 = md5_file($imgPath);
        $filename = env('COSV5_UPLOAD_PATH') . $root_dir . '/' . date('Ymd') . '/' . ($fileName ?? $md5) . '.png';

        $disk = Storage::disk(Picture::DRIVER_TENCENT_COS);
        $res = $disk->put($filename, file_get_contents($imgPath));

        if ($res) {
            unlink($imgPath);//删除
            return $filename;
        } else {
            return "";
        }
    }
}
